# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: jaewon

from coin.strategy.marketsim.core import spec_pb2


def generate_sim_stat(positions, orders, executions, product_specs):
  product_spec_map = {pspec.symbol: pspec for pspec in product_specs}

  product_stats = {}
  for pos in positions:
    pstat = spec_pb2.PerProductSimStatProto()
    pstat.symbol = pos.symbol
    pstat.settlement_currency = pos.settlement_currency
    pstat.pnl_before_fee = pos.unrealized_pnl + pos.realized_pnl
    pstat.gross_pnl = pos.gross_pnl
    pstat.gross_fee = pos.gross_fee
    product_stats[pos.symbol] = pstat

  for order in orders:
    if order.accept_ts == 0:
      continue

    pstat = product_stats[order.symbol]
    pspec = product_spec_map[order.symbol]
    money_multiplier = pspec.money_multiplier or 1.

    filled = (order.fill_pq_sum != 0.)
    pstat.num_orders += 1
    pstat.num_filled_orders += filled
    if order.side == spec_pb2.ORDER_SIDE_BUY:
      pstat.num_buy_orders += 1
      pstat.num_buy_filled_orders += filled
    else:
      pstat.num_sell_orders += 1
      pstat.num_sell_filled_orders += filled

    if not pspec.inverse:
      volume = order.order_qty * order.price * money_multiplier
    else:
      volume = order.order_qty / order.price * money_multiplier

    pstat.submitted_volume += volume
    if order.side == spec_pb2.ORDER_SIDE_BUY:
      pstat.buy_submitted_volume += volume
    else:
      pstat.sell_submitted_volume += volume

  for execution in executions:
    if execution.exec_type != spec_pb2.EXEC_TYPE_FILLED:
      continue

    pstat = product_stats[execution.symbol]
    pspec = product_spec_map[order.symbol]
    money_multiplier = pspec.money_multiplier or 1.

    if not pspec.inverse:
      volume = execution.fill_qty * execution.fill_price * money_multiplier
    else:
      volume = execution.fill_qty / execution.fill_price * money_multiplier

    pstat.volume += volume
    if execution.side == spec_pb2.ORDER_SIDE_BUY:
      pstat.buy_volume += volume
    else:
      pstat.sell_volume += volume

    if execution.fill_taker:
      pstat.taker_volume += volume
      if execution.side == spec_pb2.ORDER_SIDE_BUY:
        pstat.buy_taker_volume += volume
      else:
        pstat.sell_taker_volume += volume

  for symbol, pstat in product_stats.items():
    if pstat.num_orders != 0:
      pstat.order_fill_ratio = pstat.num_filled_orders / pstat.num_orders
    if pstat.num_buy_orders != 0:
      pstat.buy_order_fill_ratio = pstat.num_buy_filled_orders / pstat.num_buy_orders
    if pstat.num_sell_orders != 0:
      pstat.sell_order_fill_ratio = pstat.num_sell_filled_orders / pstat.num_sell_orders

    if pstat.submitted_volume != 0:
      pstat.fill_ratio = pstat.volume / pstat.submitted_volume
    if pstat.buy_submitted_volume != 0:
      pstat.buy_fill_ratio = pstat.buy_volume / pstat.buy_submitted_volume
    if pstat.sell_submitted_volume != 0:
      pstat.sell_fill_ratio = pstat.sell_volume / pstat.sell_submitted_volume

    if pstat.volume != 0:
      pstat.taker_ratio = pstat.taker_volume / pstat.volume
    if pstat.buy_volume != 0.:
      pstat.buy_taker_ratio = pstat.buy_taker_volume / pstat.buy_volume
    if pstat.sell_volume != 0.:
      pstat.sell_taker_ratio = pstat.sell_taker_volume / pstat.sell_volume

    if pstat.volume != 0:
      pstat.return_per_volume = pstat.gross_pnl / pstat.volume
      pstat.return_per_volume_before_fee = pstat.pnl_before_fee / pstat.volume

  sim_stat = spec_pb2.SimStatProto()
  sim_stat.product_stats.extend([product_stats[symbol] for symbol in sorted(product_stats.keys())])
  return sim_stat
